package com.small.sentinel;

import com.small.common.designPattern.behavioral.chainOfResponsibility.slotchain.ProcessorSlot;
import com.small.sentinel.context.ContextUtil;
import com.small.sentinel.context.NullContext;
import com.small.sentinel.node.Node;
import com.small.sentinel.slotchain.ResourceWrapper;
import com.small.sentinel.slots.SentinelResource;

public class CtEntry extends Entry {

    /**
     * 父节点
     */
    protected Entry parent = null;

    /**
     * 子节点
     */
    protected Entry child = null;

    /**
     * 执行链
     */
    protected ProcessorSlot<Object> chain;

    /**
     * 上下文
     */
    protected Context context;

    CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
        super(resourceWrapper);
        this.chain = chain;
        this.context = context;
        this.setUpEntryFor(context);
    }

    private void setUpEntryFor(Context context) {

        if (context instanceof NullContext) {
            return;
        }
        this.parent = context.getCurEntry();
        if (parent != null) {
            ((CtEntry) parent).child = this;
        }
        context.setCurEntry(this);
    }


    @Override
    public Node getLastNode() {
        return parent == null ? null : parent.getCurNode();
    }

    @Override
    public void exit(int count, Object... args) throws ErrorEntryFreeException {
        trueExit(count, args);
    }

    @Override
    protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
        exitForContext(context, count, args);
        return parent;
    }

    /**
     * 退出上下文
     * @param context 上下文
     * @param count 数量
     * @param args 参数
     * @throws ErrorEntryFreeException
     */
    protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
        if(context != null && !(context instanceof NullContext)){

            //如果上下文中的入口不是当前入口（节点链退出错误）
            if (context.getCurEntry() != this){
                String curEntryNameInContext = context.getCurEntry() == null ? null
                        : context.getCurEntry().getResourceWrapper().getName();
                //清除之前的的入口
                CtEntry e = (CtEntry) context.getCurEntry();
                while (e != null) {
                    e.exit(count, args);
                    e = (CtEntry) e.parent;
                }
                String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
                                + ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
                        resourceWrapper.getName());
                throw new ErrorEntryFreeException(errorMessage);
            }else {
                SentinelResource.SentinelResourceBuilder sentinelResourceBuilder = new SentinelResource.SentinelResourceBuilder(context, resourceWrapper).appendArgs(args).appendCount(count);
                if (chain != null) {
                    try {
                        chain.exit(sentinelResourceBuilder.builder());
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                    //设置当前入口为父节点
                    context.setCurEntry(parent);
                    //清除当前节点
                    if (parent != null) {
                        ((CtEntry) parent).child = null;
                    }
                    //如果进入时使用的是默认的上下文 需要清除
                    if (parent == null && ContextUtil.isDefaultContext(context)){
                        ContextUtil.exit();
                    }
                    clearEntryContext();
                }
            }
        }
    }

    protected void clearEntryContext() {
        this.context = null;
    }
}